package com.blkj.iam.system.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.blkj.iam.common.constant.SsoConstants;
import com.blkj.iam.common.constant.SsoUrlConstants;
import com.blkj.iam.common.result.ApiResult;
import com.blkj.iam.common.util.HttpSender;
import com.blkj.iam.config.property.SyncDataProperties;
import com.blkj.iam.core.security.util.SecurityUtils;
import com.blkj.iam.system.mapper.*;
import com.blkj.iam.system.model.dto.TransPermissionDTO;
import com.blkj.iam.system.model.entity.*;
import com.blkj.iam.system.model.vo.ShowDeptInfoVO;
import com.blkj.iam.system.model.vo.ShowRoleInfoVO;
import com.blkj.iam.system.model.vo.ShowUserInfoVO;
import com.blkj.iam.system.service.ClientService;
import com.blkj.iam.system.service.ServerDataSyncService;
import com.blkj.iam.system.service.SysSyncDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @description: 用户数据同步实现类
 * @author: Ethan
 * @create: 2025-05-08
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class ServerDataSyncServiceImpl implements ServerDataSyncService {

    private final ClientMapper clientMapper;
    private final UserMapper userMapper;
    private final DeptMapper deptMapper;
    private final RoleMapper roleMapper;
    private final MenuMapper menuMapper;
    private final SysSyncDataService syncDataService;
    private final SyncDataProperties dataProperties;
    private final ClientService clientService;


    @Override
    public void addUser(User entity, List<Long> roleIds) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            Long deptId = entity.getDeptId();
            Dept dept = deptMapper.selectById(deptId);

            List<Role> roleList = roleMapper.selectList(new QueryWrapper<Role>().in("id", roleIds));
            List<String> roleIdList = roleList.stream().map(Role::getIdStr).toList();

            ShowUserInfoVO userInfoVO = User.toShowUserInfo(entity);
            userInfoVO.setDepartId(dept.getIdStr());
            userInfoVO.setOrgCode(dept.getCode());

            JSONObject object = new JSONObject();
            object.putOpt("userInfo", userInfoVO);
            object.putOpt("roleIds", roleIdList);

            jsonStr = JSONUtil.toJsonStr(object);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.ADD_USER, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("用户新增数据同步失败：{}", result);
                syncDataService.saveUserAdd(jsonStr, result);
            }

            // 同步ID
            entity.setIdStr(parseObj.getStr(SsoConstants.RSP_DATA));
            userMapper.updateById(entity);

            log.info("用户新增数据同步成功");
        } catch (Exception e) {
            log.error("用户新增数据同步失败", e);
            syncDataService.saveUserAdd(jsonStr, e.getMessage());
        }
    }


    @Override
    public void editUser(User user, List<Long> roleIds) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            User entity = userMapper.selectById(user.getId());
            Long deptId = entity.getDeptId();
            Dept dept = deptMapper.selectById(deptId);

            List<Role> roleList = roleMapper.selectList(new QueryWrapper<Role>().in("id", roleIds));
            List<String> roleIdList = roleList.stream().map(Role::getIdStr).toList();

            ShowUserInfoVO userInfoVO = User.toShowUserInfo(entity);
            userInfoVO.setDepartId(dept.getIdStr());
            userInfoVO.setOrgCode(dept.getCode());

            JSONObject object = new JSONObject();
            object.putOpt("userInfo", userInfoVO);
            object.putOpt("roleIds", roleIdList);

            jsonStr = JSONUtil.toJsonStr(object);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.EDIT_USER, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("用户编辑数据同步失败：{}", result);
                syncDataService.saveUserEdit(jsonStr, result);
            }
            log.info("用户编辑数据同步成功");
        } catch (Exception e) {
            log.error("用户编辑数据同步失败", e);
            syncDataService.saveUserEdit(jsonStr, e.getMessage());
        }
    }

    @Override
    public ApiResult deleteUsers(List<Long> ids) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return ApiResult.OK();
        }

        String jsonStr = null;
        try {
            JSONArray array = new JSONArray();
            for (Long userId : ids) {
                User user = userMapper.selectById(userId);
                array.put(user.getIdStr());
            }

            jsonStr = JSONUtil.toJsonStr(array);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.DEl_USER, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("用户删除数据同步失败：{}", result);
                syncDataService.saveUserDel(jsonStr, result);
                return ApiResult.error("用户删除数据同步失败");
            }

            log.info("用户删除数据同步成功");
            return ApiResult.OK();
        } catch (Exception e) {
            log.error("用户删除数据同步失败", e);
            syncDataService.saveUserDel(jsonStr, e.getMessage());
            return ApiResult.error(e.getMessage());
        }
    }

    @Override
    public void addRole(Role role) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            ShowRoleInfoVO showRoleInfo = Role.toShowRoleInfo(role);
            jsonStr = JSONUtil.toJsonStr(showRoleInfo);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.ADD_ROLE, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("角色新增数据同步失败：{}", result);
                syncDataService.saveRoleAdd(jsonStr, result);
            }

            // 同步ID
            role.setIdStr(parseObj.getStr(SsoConstants.RSP_DATA));
            roleMapper.updateById(role);

            log.info("角色新增数据同步成功：{}", result);
        } catch (Exception e) {
            log.error("角色新增数据同步失败", e);
            syncDataService.saveRoleAdd(jsonStr, e.getMessage());
        }
    }

    @Override
    public void editRole(Role role) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            Role entity = roleMapper.selectById(role.getId());
            ShowRoleInfoVO showRoleInfo = Role.toShowRoleInfo(entity);
            jsonStr = JSONUtil.toJsonStr(showRoleInfo);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.EDIT_ROLE, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("角色编辑数据同步失败：{}", result);
                syncDataService.saveRoleEdit(jsonStr, result);
            }

            log.info("角色编辑数据同步成功：{}", result);
        } catch (Exception e) {
            log.error("角色编辑数据同步失败", e);
            syncDataService.saveRoleEdit(jsonStr, e.getMessage());
        }
    }

    @Override
    public ApiResult deleteRole(Role role) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return ApiResult.OK();
        }

        String jsonStr = null;
        try {
            JSONArray array = new JSONArray();
            array.put(role.getIdStr());

            jsonStr = JSONUtil.toJsonStr(array);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.DEl_ROLE, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("角色删除数据同步失败：{}", result);
                syncDataService.saveRoleDel(jsonStr, result);
                return ApiResult.error("角色删除数据同步失败");
            }

            log.info("角色删除数据同步成功");
            return ApiResult.OK();
        } catch (Exception e) {
            log.error("角色删除数据同步失败", e);
            syncDataService.saveRoleDel(jsonStr, e.getMessage());
            return ApiResult.error(e.getMessage());
        }
    }

    @Override
    public void addDept(Dept dept) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            ShowDeptInfoVO showDeptInfo = Dept.toShowDeptInfo(dept);
            jsonStr = JSONUtil.toJsonStr(showDeptInfo);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.ADD_DEPT, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("部门新增数据同步失败：{}", result);
                syncDataService.saveDeptAdd(jsonStr, result);
            }

            // 同步ID
            dept.setIdStr(parseObj.getStr(SsoConstants.RSP_DATA));
            deptMapper.updateById(dept);

            log.info("部门新增数据同步成功");
        } catch (Exception e) {
            log.error("部门新增数据同步失败", e);
            syncDataService.saveDeptAdd(jsonStr, e.getMessage());
        }
    }

    @Override
    public void editDept(Dept dept) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            Dept entity = deptMapper.selectById(dept.getId());
            ShowDeptInfoVO showDeptInfo = Dept.toShowDeptInfo(entity);
            jsonStr = JSONUtil.toJsonStr(showDeptInfo);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.EDIT_DEPT, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("部门编辑数据同步失败：{}", result);
                syncDataService.saveDeptEdit(jsonStr, result);
            }

            log.info("部门编辑数据同步成功");
        } catch (Exception e) {
            log.error("部门编辑数据同步失败", e);
            syncDataService.saveDeptEdit(jsonStr, e.getMessage());
        }
    }

    @Override
    public ApiResult deleteDept(String deptId) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return ApiResult.OK();
        }

        String jsonStr = null;
        try {
            JSONArray array = new JSONArray();
            array.put(deptId);

            jsonStr = JSONUtil.toJsonStr(array);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.DEL_DEPT, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("部门删除数据同步失败：{}", result);
                syncDataService.saveDeptDel(jsonStr, result);
                return ApiResult.error("部门删除数据同步失败");
            }

            log.info("部门删除数据同步成功");
            return ApiResult.OK();
        } catch (Exception e) {
            log.error("部门删除数据同步失败", e);
            syncDataService.saveDeptDel(jsonStr, e.getMessage());
            return ApiResult.error(e.getMessage());
        }
    }

    @Override
    public void addMenu(Menu menu) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            TransPermissionDTO transPermission = Menu.toTransPermission(menu);
            jsonStr = JSONUtil.toJsonStr(transPermission);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.ADD_MENU, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("菜单新增数据同步失败：{}", result);
                syncDataService.saveMenuAdd(jsonStr, result);
            }

            // 同步ID
            menu.setIdStr(parseObj.getStr(SsoConstants.RSP_DATA));
            Menu selected = menuMapper.selectOne(new LambdaQueryWrapper<Menu>().eq(Menu::getIdStr, menu.getIdStr()));
            if (ObjectUtil.isNotEmpty(selected)) {
                menu.setParentIdStr(selected.getIdStr());
            }
            menuMapper.updateById(menu);

            log.info("菜单新增数据同步成功");
        } catch (Exception e) {
            log.error("菜单新增数据同步失败", e);
            syncDataService.saveMenuAdd(jsonStr, e.getMessage());
        }
    }

    @Override
    public void editMenu(Menu menu) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            Menu entity = menuMapper.selectById(menu.getId());
            TransPermissionDTO transPermission = Menu.toTransPermission(entity);
            jsonStr = JSONUtil.toJsonStr(transPermission);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.EDIT_MENU, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("菜单编辑数据同步失败：{}", result);
                syncDataService.saveMenuEdit(jsonStr, result);
            }

            // 同步ID
            entity.setIdStr(parseObj.getStr(SsoConstants.RSP_DATA));
            Menu selected = menuMapper.selectOne(new LambdaQueryWrapper<Menu>().eq(Menu::getIdStr, entity.getIdStr()));
            if (ObjectUtil.isNotEmpty(selected)) {
                entity.setParentIdStr(selected.getIdStr());
            }
            menuMapper.updateById(entity);

            log.info("菜单编辑数据同步成功");
        } catch (Exception e) {
            log.error("菜单编辑数据同步失败", e);
            syncDataService.saveMenuEdit(jsonStr, e.getMessage());
        }
    }

    @Override
    public void deleteMenu(String menuId) {
        List<String> excludeAppIds = dataProperties.getExcludeAppIds();
        if (excludeAppIds.contains(getAppId())) {
            return;
        }

        String jsonStr = null;
        try {
            JSONArray array = new JSONArray();
            array.put(menuId);

            jsonStr = JSONUtil.toJsonStr(array);
            Long clientId = SecurityUtils.getClientId();
            Client client = clientMapper.selectById(clientId);
            HttpSender sender = new HttpSender();
            String result = sender.post(client.getAddress() + SsoUrlConstants.DEL_MENU, jsonStr);

            JSONObject parseObj = JSONUtil.parseObj(result);
            if (parseObj.getInt(SsoConstants.RSP_CODE) != HttpStatus.HTTP_OK) {
                log.error("菜单删除数据同步失败：{}", result);
                syncDataService.saveMenuDel(jsonStr, result);
            }

            log.info("菜单删除数据同步成功");
        } catch (Exception e) {
            log.error("菜单删除数据同步失败", e);
            syncDataService.saveMenuDel(jsonStr, e.getMessage());
        }
    }

    /***
     * 获取应用ID
     * @param
     * @return: java.lang.String
     */
    private String getAppId() {
        Long clientId = SecurityUtils.getClientId();
        Client client = clientService.getById(clientId);
        return client.getAppId();
    }

}
